优化片段着色器

根据底层 GPU 架构,对于每个绘制调用,GPU 可以并行执行很多阶段的渲染工作,例如顶点处理、片段处理和内存读取。绘制调用会等待,直到所有片段都得到处理为止。如果片段着色器的执行速度比顶点着色器或其他阶段慢,则其他阶段需要等待片段着色器执行完毕。

您可以通过以下方法优化片段着色器:

降低片段着色器的精度

如果性能瓶颈在于片段着色,精度从两个周期降至一个周期会提升 GPU 一半的性能。

要降低像素着色器的精度:

  1. 素材库 (Library) 中选择资源文件 (Resource Files) > 着色器 (Shaders) 并打开您要降低其精度的片段着色器。
    着色器源编辑器 (Shader Source Editor) 窗口即会打开。
  2. 对于 precision,使用适当的值范围:
    • lowp 适用于颜色范围(RGB 数据范围 [0..1])和强度范围 [0..1] 等数据,但不适用于需要更高精度的纹理坐标等数据。lowp 支持范围是 [-2..2],小数点后保留 8 位精度。
    • mediump 适用于大多数渲染,矩阵需要具有更高的精度,原因在于浮点值相对较小。
    • highp 包含精确的 3D 渲染表示,包括矩阵。

    例如,对于片段着色器,请使用代码:
    uniform sampler2D Texture;
    uniform lowp float BlendIntensity;
    varying mediump vec2 vTexCoord;
    
    void main()
    {
        precision lowp float;
    
        vec4 color = texture2D(Texture, vTexCoord);
        gl_FragColor.rgba = color.rgba * BlendIntensity;
    }

    请勿使用此代码:
    uniform sampler2D Texture;
    uniform mediump float BlendIntensity;  //与 lowp 相比,mediump 将周期次数增加了一倍
    varying mediump vec2 vTexCoord;
    
    void main()
    {
        precision mediump float;  //与 lowp 相比,mediump 将周期次数增加了一倍
    
        vec4 color = texture2D(Texture, vTexCoord);
        gl_FragColor.rgba = color.rgba * BlendIntensity;
    }

将像素计算切换到顶点计算

使用顶点着色器计算需要保持不变并只需要计算几次的值。对光照计算执行类似操作可以将一个顶点的结果插值到另一个顶点中而且不会导致质量明显下降,因为顶点覆盖范围通常比片段覆盖范围小很多(除了高密度的几何结构以外)。

例如,对于顶点着色器,请使用代码:

attribute vec3 kzPosition;
attribute vec2 kzTextureCoordinate0;
uniform highp mat4 kzProjectionCameraWorldMatrix;
uniform mediump float kzTime;

varying mediump vec2 vTexCoord;
varying lowp vec4 vColor;

void main()
{
    precision mediump float;
    //只对每个顶点执行三角函数运算,例如,
    //对于 quad 3 * 2 times (2 个三角形,每个三角形各有 3 个顶点)
    vColor = vec4(sin(kzTime));
    gl_Position = kzProjectionCameraWorldMatrix * vec4(kzPosition.xyz, 1.0);
}

例如,对于片段着色器,请使用代码:

varying lowp vec4 vColor;

void main()
{
    precision lowp float;

    //对于写入的每个片段,应用具有相同精度 (lowp -> lowp) 的
    //常数插值分配。在大多数 GPU 上,分配不应超过
    //一个周期。
    gl_FragColor.rgba = vColor;
}

例如,对于顶点着色器,请勿使用代码:

attribute vec3 kzPosition;
uniform highp mat4 kzProjectionCameraWorldMatrix;

void main()
{
    precision mediump float;
    //顶点着色器会输出位置并在片段着色器中执行计算,
    //当片段数量超过顶点数量时,这并不是一种明智的做法。
    gl_Position = kzProjectionCameraWorldMatrix * vec4(kzPosition.xyz, 1.0);
}

例如,对于片段着色器,请勿使用代码:

uniform mediump float kzTime;

void main()
{
    precision lowp float;

    //对于写入的每个片段,额外的三角函数 sin()
    //会被执行。三角函数会消耗大量的资源 - 具体取决于 GPU,
    //每个片段需要使用多个 GPU 周期。实际上,这样做的结果与
    //将结果存储为 varying 是一样的。
    gl_FragColor.rgba = vec4(sin(kzTime));
}

另请参阅

减少着色器切换

使用二进制着色器

并行加载资源

着色器最佳实践

排除应用程序的性能问题

最佳实践